import re
from odoo import fields, models, api, _
from odoo.exceptions import UserError, ValidationError


class SbCec(models.Model):
    _name = 'sb.cec'
    _description = 'customer engineering change'
    _inherit = ['portal.mixin', 'mail.thread.main.attachment', 'mail.activity.mixin']

    name = fields.Char(string='Name', compute='_compute_name', store=True)
    pn = fields.Char(string='PN')
    mpn = fields.Many2one('product.template', string='MPN')
    # mo = fields.Many2many('sale.order', string='Sales Orders')
    cec_line_ids = fields.One2many('sb.cec.order.line', string='Sales Orders',inverse_name="cec_id")
    upgrade_type = fields.Selection(
        [('internal', 'Internal upgrade'),
         ('external', 'External upgrade')], string='CEC Type', default='external', required=True)
    mpn_rev = fields.Char(string='MPN Revision', help='Revision of the MPN', related='mpn.versions')
    change_reason = fields.Text(string='Change Reason', help='Reason for the change')
    need_update_pn = fields.Boolean(string='Need Update PN', default=False, help='If True, the PN will be updated based on MPN or vice versa.')
    n_pn_rev = fields.Char(string='New PN Revision', help='New Customer PN Revision after update')
    n_pn = fields.Char(string='New PN', help='New Customer PN after update')
    n_mpn_rev = fields.Char(
        string='New MPN Revision',
        compute='_compute_n_mpn_rev',
        store=True, readonly=False)
    state = fields.Selection([
        ('draft', 'Draft'),
        ('approval', 'Approval'),
        ('done', 'Done'),
        ('cancelled', 'Cancelled')
    ], string='State', default='draft', required=True, help="The state of the CEC record.", tracking=True)

    sb_workflow_id = fields.Many2one('sb.workflow', string="Current Approval Workflow", copy=False)
    sb_workflow_state = fields.Selection(related="sb_workflow_id.state", readonly=True, string="Approval Status",
                                         store=True)
    sb_workflow_item_ids = fields.One2many(related="sb_workflow_id.sb_workflow_item_ids", string="Approval Items")
    sb_workflow_attachments_ids = fields.One2many(related="sb_workflow_id.sb_workflow_attachments_ids",
                                                  string="Approval Attachments")
    sb_workflow_template_id = fields.Many2one('sb.workflow.template', string="Approval Workflow Template")
    sb_workflow_ids = fields.One2many('sb.workflow', 'sale_order_id', string="Approval Workflows")
    sb_workflow_ids_sum = fields.Integer(compute="_compute_sb_workflow_ids_sum")
    customer_item_code = fields.Many2one('customer.item.code', string='Customer Item Code', store=True)
    customer = fields.Many2one('res.partner', string='Customer', related='customer_item_code.customer_id')
    remarks = fields.Text(string='Remarks')
    change_description = fields.Text(string='Change Description')
    confirmed_date = fields.Date(string='Confirmed Date', help='Date when the CEC was confirmed')
    s_actual_quantity = fields.Float(string='Auantity Available',readonly=True)
    pn_desc = fields.Char(string="PN Description")
    pn_version = fields.Char(string="PN Version")
    new_pn_desc = fields.Char(string="New PN Description")

    change_mo_ids = fields.Many2many('sale.order', )
    s_document_ids = fields.One2many('documents.document', 'res_id', string='Documents',
                                     domain="[('res_model', '=', 'sale.order'), ('res_id', '=', id)]")

    def default_get(self, fields_list):
        result = super().default_get(fields_list)
        if self.env.context.get('default_upgrade_type','') == 'external':
            result['sb_workflow_template_id'] = self.env.ref("sb_sale_control.workflow_data_cec_external").id
        elif self.env.context.get('default_upgrade_type','') == 'internal':
            result['sb_workflow_template_id'] = self.env.ref("sb_sale_control.workflow_data_cec_internal").id
        return result


    @api.onchange('mpn')
    def _onchange_mpn(self):
        product_id = self.env['product.product'].search([('product_tmpl_id','=',self.mpn.id)])
        self.s_actual_quantity = product_id.s_actual_quantity

    @staticmethod
    def _parse_rev(rev):
        """
        把 'A1' / 'B12' 拆成字母部分和数字部分
        返回 (letter, number)
        """
        if not rev:
            return 'A', 0
        m = re.match(r'^([A-Z]+)(\d+)$', rev.upper())
        if m:
            return m.group(1), int(m.group(2))
        return 'A', 0

    @api.depends('upgrade_type', 'mpn_rev')
    def _compute_n_mpn_rev(self):
        for rec in self:
            letter, number = self._parse_rev(rec.mpn_rev)

            if rec.upgrade_type == 'internal':
                # 数字 +1，字母不变
                number += 1
            elif rec.upgrade_type == 'external':
                # 字母 +1，数字归零
                letter = chr(ord(letter) + 1)
                number = 0

            rec.n_mpn_rev = f'{letter}{number}'

    @api.depends('mpn', 'pn')
    def _compute_name(self):
        for rec in self:
            if rec.mpn and rec.pn:
                rec.name = f"{rec.mpn.name} - {rec.pn}"
            elif rec.mpn:
                rec.name = rec.mpn.name
            elif rec.pn:
                rec.name = rec.pn
            else:
                rec.name = False

    def action_view_change_mo(self):
        # 跳转已变更MO列表

        return {
            'name': 'Change MO',
            'type': 'ir.actions.act_window',
            'res_model': 'sale.order',
            'view_mode': 'tree',
            'target': 'current',
            'domain': [('id', 'in', self.change_mo_ids.ids)],
        }


    def action_view_documents(self):
        self.ensure_one()
        return {
            'name': 'Documents',
            'type': 'ir.actions.act_window',
            'res_model': 'documents.document',
            'view_mode': 'kanban,tree,form',
            'domain': [
                ('res_model', '=', 'sb.cec'),
                ('res_id', '=', self.id),
            ],
            'context': {
                'default_res_model': 'sb.cec',
                'default_res_id': self.id,
                'create': False,
                'searchpanel_default_folder_id': self.env.ref('sb_sale_control.sb_cec_internal_folder').id  if self.upgrade_type=='internal' else\
                    self.env.ref('sb_sale_control.sb_cec_external_folder').id ,
            },
            'target': 'current',
        }

    def action_open_document_upload_wizard(self):
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': 'Upload Document',
            'res_model': 'document.upload.wizard',
            'view_mode': 'form',
            'target': 'new',
            'context': {
                'default_res_model': 'sb.cec',
                'default_res_id': self.id,
                'default_folder_id': self.env.ref('sb_sale_control.sb_cec_internal_folder').id if self.upgrade_type=='internal' else\
                    self.env.ref('sb_sale_control.sb_cec_external_folder').id,
                'default_tag_id': self.with_context(lang='en_US').env['documents.tag'].search(
                    [('name', '=', 'CEC Internal')]).id if self.upgrade_type=='internal' else
                self.with_context(lang='en_US').env['documents.tag'].search(
                    [('name', '=', 'CEC External')]).id
            },
        }

    def confirm(self):
        """
        确认 CEC，执行必要的逻辑
        """
        # 这里可以添加确认逻辑，比如更新相关字段或触发其他操作
        self.ensure_one()  # 确保只处理单个记录
        self.sudo()
        if not self.mpn or not self.pn:
            raise ValidationError(_("MPN and PN must be set before confirming CEC."))
        if self.need_update_pn and (not self.n_pn or not self.n_mpn_rev):
            raise ValidationError(_("New PN and New MPN Revision must be set before confirming CEC."))
        new_product_temp_id,new_customer_code_id = self.generate_mpn()
        if not new_product_temp_id:
            raise ValidationError(_("Failed to generate new MPN. Please check the input data."))
        for line in self.cec_line_ids.filtered(lambda a:a.is_change==True):
            sale_order = line.mo_id
            # 更新销售订单中的产品
            sale_order.action_unlock()
            sale_order.first_line.write({
                'product_id': new_product_temp_id.product_variant_id.id,
                's_customer_item_code':new_customer_code_id.id,
            })
            if sale_order.state != 'draft':
                # 如果订单不是草稿状态，锁定订单
                sale_order.action_lock()
        self.change_mo_ids = self.cec_line_ids.filtered(lambda a:a.is_change==True).ids
        # 记录更新后new product
        # self.new_pn_des = new_product_temp_id.id
        self.new_pn_desc = new_customer_code_id.customer_item_desc
        self.state='done'

    def generate_mpn(self):
        # 字段检查

        uom_id = self.env['uom.uom'].sudo().search([('name', '=', 'PCS')], limit=1)

        m = re.search(r'(?<=L)[A-Z]?(\d+)', self.mpn.name)
        if not m:
            raise ValidationError(_("MPN name does not contain a valid sequence number."))
        seq = m.group(1)

        new_product_id = self.env['product.template'].sudo().create({
            'name': 'new',
            'purchase_ok': True,
            'sale_ok': True,
            'detailed_type': 'product',
            'categ_id': self.env.ref('sb_sale.finished_product_type').id or 85,
            'route_ids': [self.env.ref('mrp.route_warehouse0_manufacture').id],
            'tracking': 'lot',
            'uom_id': uom_id.id or 1,
            'uom_po_id': uom_id.id or 1,
            "application_product_id": self.mpn.application_product_id.id,
            'default_code': 'W' + seq + self.n_mpn_rev,
            'versions': self.n_mpn_rev
        })
        new_product_id.is_manufacture = True
        new_product_id._init_uov_records()
        # 客户部件信息创建
        customer_item_code = self.mpn.customer_item_code_ids[0]
        new_customer_code_id = self.env['customer.item.code'].sudo().create({
            'customer_id': customer_item_code.customer_id.id,
            'code': self.n_pn if self.need_update_pn and self.n_pn else customer_item_code.pn,
            'version': self.n_pn_rev if self.need_update_pn and self.n_pn_rev else customer_item_code.version,
            'product_id': new_product_id.id,
            'customer_item_desc': self.new_pn_desc if self.need_update_pn and self.new_pn_desc else customer_item_code.customer_item_desc,
            'recent_price': 0,
        })
        new_product_id.customer_item_code_ids = [(4, new_customer_code_id.id)]
        new_product_id.name = new_product_id.default_code if new_product_id.active else new_product_id.before_approval_code
        customer_item_code.active=False
        return new_product_id,new_customer_code_id

    @api.onchange('mpn', 'pn')
    def _onchange_pn_or_mpn(self):
        # 獲取觸發 onchange 時的當前值
        current_mpn = self.mpn
        current_pn = self.pn

        # 初始化相關字段，但只在滿足某些條件時才清空
        # 避免在用戶輸入值時立即清空
        # self.mo = [(5, 0, 0)]  # 清空 Many2many 字段，使用 (5,0,0) 更明確
        self.cec_line_ids = None
        self.customer_item_code = False
        is_new_record = not self.id  # 或者 not self._origin.id (取決於Odoo版本和具體上下文)

        mpn_changed_by_user = (is_new_record and current_mpn) or \
                              (not is_new_record and self._origin.mpn != current_mpn)

        pn_changed_by_user = (is_new_record and current_pn) or \
                             (not is_new_record and self._origin.pn != current_pn)

        # 處理邏輯
        if mpn_changed_by_user and current_mpn:
            # 清空 PN，防止上次輸入的 PN 干擾（如果用戶先輸入 PN 又輸入 MPN）
            self.pn = False

            customer_item_codes = self.env['customer.item.code'].search([('product_id', '=', current_mpn.id)], limit=1)
            if customer_item_codes:
                self.customer_item_code = customer_item_codes.id
                self.pn = customer_item_codes.code  # 找到對應的PN並填寫
                self.pn_desc = customer_item_codes.customer_item_desc  # 找到對應的PN_DESC並填寫
                self.pn_version = customer_item_codes.version  # 找到對應的PN_VERSION並填寫
                # self.n_pn = customer_item_codes.code  # 同時更新 n_pn
                # self.n_pn_rev = customer_item_codes.version  # 同時更新 n_pn_rev
            sale_orders = self.env['sale.order'].search([('product_id', '=', current_mpn.id), ('status', 'not in', ['cancelled', 'completed', 'closed'])])
            # self.mo = [(6, 0, sale_orders.ids)]
            lines = []
            for mo_id in sale_orders:
                lines.append((0,0,{
                    'mo_id':mo_id.id,
                    'product_id':mo_id.product_id.id,
                    'create_date':mo_id.create_date,
                    'po':mo_id.po,
                    'pn':mo_id.pn,
                    'qty':mo_id.qty,
                    'commitment_date':mo_id.commitment_date,
                    'status':mo_id.status,
                }))
            self.cec_line_ids = None
            self.cec_line_ids = lines

        elif pn_changed_by_user and current_pn:
            # 用戶輸入了PN
            # 清空 MPN，防止上次輸入的 MPN 干擾
            self.mpn = False
            customer_item_codes = self.env['customer.item.code'].search([('code', '=', current_pn)], limit=1)
            if not customer_item_codes:
                raise ValidationError(_("No customer item code found for the given PN."))

            else:
                self.mpn = customer_item_codes.product_id.id  # 找到對應的成品並填寫
                # self.n_pn = current_pn
                self.customer_item_code = customer_item_codes.id
                # self.n_pn_rev = customer_item_codes.version
                # 只有當找到了 MPN 後，才根據 MPN 查找銷售訂單
                sale_orders = self.env['sale.order'].search([('order_line.product_id', '=', self.mpn.id), ('status', 'not in', ['cancelled', 'completed', 'closed'])])
                # self.mo = [(6, 0, sale_orders.ids)]
                lines = []
                for mo_id in sale_orders:
                    lines.append((0, 0, {
                        'mo_id': mo_id.id,
                        'product_id': mo_id.product_id.id,
                        'create_date': mo_id.create_date,
                        'po': mo_id.po,
                        'pn': mo_id.pn,
                        'qty': mo_id.qty,
                        'commitment_date': mo_id.commitment_date,
                        'status': mo_id.status,
                    }))
                self.cec_line_ids = None
                self.cec_line_ids = lines

        else:
            # 兩個都沒有輸入，或者用戶清空了其中一個，或者用戶同時清空了兩個
            self.mpn = False
            self.pn = False
            self.n_pn = False
            self.customer_item_code = False
            self.n_pn_rev = False
            # self.mo = [(5, 0, 0)]  # 確保清空 Many2many
            self.cec_line_ids = None

    def action_view_workflow(self):
        """
        打開與當前單據關聯的所有審批流程。
        """
        self.ensure_one()

        # 獲取與當前記錄關聯的審批流程
        workflows = self.env['sb.workflow'].search([
            ('res_model', '=', self._name),
            ('res_id', '=', self.id)
        ])

        # 如果只有一個審批流程，則直接打開表單視圖
        if len(workflows) == 1:
            return {
                'name': 'Approval Flow',
                'type': 'ir.actions.act_window',
                'res_model': 'sb.workflow',
                'view_mode': 'form',
                'res_id': workflows.id,
                'target': 'current',
            }
        # 如果有多個或沒有審批流程，則打開列表視圖
        else:
            return {
                'name': 'Approval Flow',
                'type': 'ir.actions.act_window',
                'res_model': 'sb.workflow',
                'view_mode': 'tree,form',
                'target': 'current',
                'domain': [
                    ('res_model', '=', self._name),
                    ('res_id', '=', self.id)
                ],
            }

    def create_workflow(self):
        """創建審批流程，安全處理不存在的字段並生成 JSON 數據"""

        # 定義獲取可讀值與標籤的函數
        def get_value_and_label(field_name, record):
            field_info = record._fields.get(field_name)
            label = field_name if field_name else field_info.string
            value = getattr(record, field_name, False)

            if field_info:
                if field_info.type == 'selection' and value is not False:
                    selection_dict = dict(field_info.selection)
                    value = selection_dict.get(value, value)
                elif field_info.type == 'many2one' and value:
                    value = value.display_name
                elif field_name == 'need_update_pn':
                    value = 'True' if value else 'False'

            return {'label': label, 'value': value if value is not False else ''}

        # 左右欄字段列表
        # todo
        left_fields = ['customer_pn', 'internal_fg', 'mpn_revision', 'cec_type']  # 根據實際修改
        right_fields = ['state', 'need_update_pn', 'new_pn', 'new_pn_revision', 'new_mpn_revision']

        # 準備明細表格數據
        list_headers = [_('Sale Orders'), _('Create Date'), _('MPN'),_('PO'),_( 'PN'), _('Qty'),_('Customer DD'), _('Status')]
        list_rows = []
        for line in self.cec_line_ids:
            mo = line.mo_id
            list_rows.append([
                mo.name or '',
                mo.create_date.strftime('%Y/%m/%d') if mo.create_date else '',
                mo.product_id.display_name if mo.product_id else '',
                mo.po or '',
                mo.pn or '',
                mo.qty or '',
                mo.commitment_date.strftime('%Y/%m/%d') if mo.commitment_date else '',
                mo.status or '',
            ])
        left_data = [{'label':_('PN'),'value':self.pn},{'label':_('CEC Type'),'value':self.upgrade_type},
                     {'label': _('MPN Revision'), 'value': self.mpn_rev},{'label':_('Status'),'value':self.state}]
        right_data = [{'label':_('Need Update PN'),'value':self.need_update_pn},
                     {'label': _('New PN'), 'value': self.n_pn}, {'label': _('New PN Revision'), 'value': self.n_pn_rev},
                      {'label': _('New MPN Revision'), 'value': self.n_mpn_rev}]
        workflow_data = {
            'l': left_data,
            'r': right_data,
            'list': {
                'headers': list_headers,
                'rows': list_rows,
            },
        }

        # 計算 workflow 編號
        workflow_sum = len(self.sb_workflow_ids) + 1 if self.sb_workflow_ids else 1

        # 創建 workflow
        new_wf_id = self.env['sb.workflow'].sudo().create({
            'name': f"{self.name}-Approval Flow-{workflow_sum}",
            'approval_time': fields.Datetime.now(),
            'workflow_type': 'sale',
            'res_id': self.id,
            'res_model': self._name,
            'workflow_data': workflow_data,
        })
        self.sb_workflow_id = new_wf_id.id

        # 創建 workflow items
        item_audit_levels = []
        for item in self.sb_workflow_template_id.sb_workflow_template_item_ids:
            new_item = self.env['sb.workflow.item'].sudo().create({
                'name': item.name,
                'user_group_ids': item.user_group_ids.ids,
                'audit_level': item.audit_level,
                'sb_workflow_id': new_wf_id.id,
                'state': 'wait_forward',
            })
            item_audit_levels.append(new_item.audit_level)

        # 將最小審批層級設置為 approval
        if item_audit_levels:
            min_level = min(item_audit_levels)
            for n_item in new_wf_id.sb_workflow_item_ids:
                if n_item.audit_level == min_level:
                    n_item.state = 'approval'
        self.state = 'approval'
        # 原product禁止下单下卡出货
        self.mpn.write({
            's_disable_down_card':True,
            's_disable_order':True,
            's_disable_delivery':True
        })
        for line in self.cec_line_ids:
            if line.inventory_bord == 'can_use':
                self.mpn.s_disable_delivery = False
                break
        return True

    @api.constrains('sb_workflow_state')
    def _check_sb_workflow_state(self):
        for record in self:
            if record.sb_workflow_state == 'fail_to_approve' and record.state in ('approval'):
                record.state = 'draft'
            if record.sb_workflow_state == 'approve' and record.state in ('draft', 'approval'):  # 审批通过自动确认订单
                record.confirm()

    def set_cancel(self):
        for record in self:
            if record.sb_workflow_state in ('approval', 'approve'):
                record.state = 'cancelled'
            else:
                raise UserError(_("Cannot cancel CEC with active or approved workflow."))

    @api.depends('sb_workflow_ids')
    def _compute_sb_workflow_ids_sum(self):
        for item in self:
            if item.sb_workflow_ids:
                item.sb_workflow_ids_sum = len(item.sb_workflow_ids)
            else:
                item.sb_workflow_ids_sum = 0


class SbCecOrderLine(models.Model):
    _name = 'sb.cec.order.line'
    _description = 'Cec Sale Order Line'

    mo_id = fields.Many2one('sale.order', string='Sale Orders')
    create_date = fields.Datetime(string="Create Date")
    product_id = fields.Many2one('product.template', string='MPN')
    po = fields.Char(string='PO')
    pn = fields.Char(string='PN')
    qty = fields.Integer(string='Qty')
    commitment_date = fields.Datetime(string='Customer DD')
    status = fields.Selection([
        ('draft', 'Draft'),
        ('active', 'Active'),
        ('invoiced', 'Invoiced'),
        ('hold', 'Hold'),
        ('closed', 'Closed'),
        ('completed', 'Completed'),
        ('part_completed', 'Part Complete'),
        ('cancelled', 'Cancelled'),
        ], string="Status",)

    is_change = fields.Boolean(string='Change',default=True)
    # 库存板
    inventory_bord = fields.Selection([('can_use','Can Use'),('repair','Repair'),('rework','Rework'),('scrap','Scrap')],
                                      string='Inventory Bords')
    # 在途物料
    meterial_transit = fields.Selection(
        [('can_use', 'Can Use'), ('no_in_transit', 'No In Transit'), ('restocking', 'Restocking')],
        string='Meterial In Transit')
    # 库存物料
    meterial_stock = fields.Selection(
        [('can_use', 'Can Use'), ('no_stock', 'No Stock')],
        string='Meterial In Stock')
    cec_id = fields.Many2one('sb.cec')

    def action_show_details(self):
        self.ensure_one()
        if self.cec_id.state != 'draft':
            raise ValidationError("Cannot edit non draft status data!")
        view = self.env.ref('sb_sale_control.sb_cec_open_line_view_form')

        return {
            'name': _('Meterial And Inventory Confirm'),
            'type': 'ir.actions.act_window',
            'view_mode': 'form',
            'res_model': 'sb.cec.order.line',
            'views': [(view.id, 'form')],
            'view_id': view.id,
            'target': 'new',
            'res_id': self.id,
        }

